/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-SpringBoot组件封装-缺省实现
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.web.autoconfigure;

import com.taipingframework.utility.thread.properties.ThreadPoolProperty;
import com.taipingframework.utility.thread.threadfactory.SmartThreadFactory;
import com.taipingframework.utility.thread.threadpool.NativeAsyncTaskExecutePool;
import com.taipingframework.utility.thread.threadpool.ThreadPoolHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 个人更喜欢将不同的配置区分开来存放，整个工程所有的配置只集中在这一个地方进行统一托管到Spring容器中
 * <p>
 * 再重新强调一次，线程池的参数配置一定要参考生产服务器的实际硬件参数，不能写死在代码里面，这里给出最优配置方案：
 * (1)CPU密集型：核心线程数 = CPU核数 + 1
 * (2)IO密集型：核心线程数 = CPU核数 x 2
 * (3)最大线程数 = CPU核数 x 2 + 1
 */
@Configuration(proxyBeanMethods = false)
public class ThreadPoolAutoConfiguration {

    /**
     * 加载线程池配置数据
     */
    @Bean("threadPoolProperty")
    @ConfigurationProperties(prefix = "application.threadpool")
    @ConditionalOnProperty(prefix = "application.threadpool", name = {"enable"}, havingValue = "true", matchIfMissing = true)
    public ThreadPoolProperty threadPoolProperty() {
        return new ThreadPoolProperty();
    }

    /**
     * 配置 微服务工程 默认的线程池。注意，这里使用了注解@Primary，代表该线程池是当前应用默认选择使用的主线程池。
     * 这里，使用阿里巴巴TTL的线程池Wrapper 将 JDK内置的线程池 给包装了一层，进一步增强线程池与ThreadLocal的功能。
     * 另外，也可以选择使用 Spring默认支持的线程池，一切均可根据实际情况进行调整。
     */
    @Primary
    @Bean(value = "asyncExecutor", destroyMethod = "shutdown")
    @ConditionalOnBean(ThreadPoolProperty.class)
    public ExecutorService asyncExecutor(ThreadPoolProperty poolProperty) {
        SmartThreadFactory threadFactory = new SmartThreadFactory(poolProperty.getThreadPrefix());
        ThreadPoolExecutor defaultExecutor = ThreadPoolHelper.createDefaultExecutor(
                poolProperty.getCorePoolSize(),
                poolProperty.getMaxPoolSize(),
                poolProperty.getKeepAliveTime(),
                poolProperty.getQueueCapacity(),
                threadFactory);
        return ThreadPoolHelper.createExecutorService(defaultExecutor);
    }

    /**
     * Spring默认支持的线程池，此处配置在这里就可以了，Spring框架内部会自动引用我们在这里配置的线程池。
     */
    @Bean(value = "taskExecutor", destroyMethod = "destroy")
    @ConditionalOnBean(ThreadPoolProperty.class)
    public ThreadPoolTaskExecutor taskExecutor(ThreadPoolProperty poolProperty) {
        SmartThreadFactory threadFactory = new SmartThreadFactory(poolProperty.getThreadPrefix());
        return ThreadPoolHelper.createAsyncExecutor(
                poolProperty.getCorePoolSize(),
                poolProperty.getMaxPoolSize(),
                poolProperty.getKeepAliveTime(),
                poolProperty.getQueueCapacity(),
                threadFactory);
    }

    /**
     * 自定义异步任务线程池
     */
    @Bean
    @ConditionalOnBean(ThreadPoolProperty.class)
    public NativeAsyncTaskExecutePool nativeAsyncTaskExecutePool(ThreadPoolProperty poolProperty) {
        return new NativeAsyncTaskExecutePool(poolProperty);
    }

    /*
     * ForkJoinPool线程池
     * 多个任务的并发执行，绝不能使用同一个ForkJoinPool线程池，会出现严重的性能问题！
     * 因此，这里将它定义为一个Java Bean注入Spring容器的行为本身就是错误的用法！！！
     *
    @Bean(value = "forkJoinPool", destroyMethod = "shutdown")
    @Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public ForkJoinPool forkJoinPool() {
        return ThreadPoolHelper.buildForkJoinPool();
    }*/

}
